home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 2 / Macwelt DVD 2.cdr / System / System-Utilities / macosx / Keep Sound Awake 1.2.dmg / KeepSoundAwake 1.2 / source / KeepSoundAwake / KeepSoundAwake.mm next >
Encoding:
Text File  |  2002-04-07  |  7.4 KB  |  225 lines

  1. /*
  2.     Copyright (c) 2002, Jonathan Feinberg
  3.     All rights reserved.
  4.     
  5.     Redistribution and use in source and binary forms, with or without
  6.     modification, are permitted provided that the following conditions are
  7.     met:
  8.     
  9.     *    Redistributions of source code must retain the above copyright
  10.         notice, this list of conditions and the following disclaimer.
  11.     *    Redistributions in binary form must reproduce the above copyright
  12.         notice, this list of conditions and the following disclaimer in the
  13.         documentation and/or other materials provided with the distribution.
  14.     *    The name of the author may not be used to endorse or promote
  15.         products derived from this software without specific prior written
  16.         permission.
  17.     
  18.     
  19.     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20.     IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  21.     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  22.     PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
  23.     OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24.     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25.     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26.     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27.     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28.     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29.     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30.  
  31.     $Id: KeepSoundAwake.mm,v 1.3 2002/04/07 17:28:32 jdf Exp $
  32.  
  33. */
  34.  
  35. #include <Foundation/Foundation.h>
  36.  
  37. // For CFRunLoop stuff
  38. #include <CoreFoundation/CoreFoundation.h>
  39.  
  40. // For ApplicationEventLoop stuff
  41. #include <Carbon/Carbon.h>
  42.  
  43. // For NSSound
  44. #include <AppKit/AppKit.h>
  45.  
  46. // For detecting whether the charger is plugged in
  47. #include <IOKit/pwr_mgt/IOPM.h>
  48. #include <IOKit/pwr_mgt/IOPMLib.h>
  49.  
  50. // For retrieving preferences
  51. #include "KSAPrefConstants.h"
  52.  
  53. // For debug and status messages
  54. #include <syslog.h>
  55.  
  56.  
  57. // Globals used in determining whether we're running on battery
  58. mach_port_t masterPort;
  59. NSString *flagsString;
  60.  
  61. // We keep things that might be affected by pref changes in these globals
  62. CFRunLoopTimerRef soundTimer;
  63. BOOL shouldWatchCharger;
  64.  
  65. // The sound to play periodically
  66. NSSound *sound;
  67.  
  68. // Are we running on battery power?
  69. // Thanks to Paul Haddad (paul@pth.com) for this code, and for the notion.
  70. int chargerIsConnected()
  71. {
  72.     NSMutableArray *array;
  73.     int newFlags;
  74.  
  75.     if (kIOReturnSuccess == IOPMCopyBatteryInfo(masterPort, (CFArrayRef *)&array)) 
  76.     {
  77.         newFlags = [[[array lastObject] objectForKey:flagsString] intValue];
  78.         [array release];
  79.     } 
  80.     else
  81.         newFlags = kIOBatteryChargerConnect;
  82.     return (newFlags & kIOBatteryChargerConnect);
  83. }
  84.  
  85. // Called periodically by soundTimer
  86. void soundTimerCallback(CFRunLoopTimerRef timer, void *info)
  87. {
  88.     if ((!shouldWatchCharger) || chargerIsConnected())
  89.         [sound play];
  90. }
  91.  
  92. // Called indirectly by DoQuitApp, below.
  93. void delayedQuit(CFRunLoopTimerRef timer, void *info)
  94. {
  95.     QuitApplicationEventLoop();
  96. }
  97.  
  98. /*
  99.     This handler is installed to handle the "QUIT" AppleEvent
  100.     It sets up a delayed call to "delayedQuit", above.  If we
  101.     do not delay the QuitApplicationEventLoop() call, then the
  102.     AppleScript infrastructure gets confused, and may wind up
  103.     starting and restarting this app, just to tell it to quit!
  104. */
  105. static pascal OSErr DoQuitApp(const AppleEvent *message, 
  106.                               AppleEvent *reply, 
  107.                               long refCon)
  108. {
  109.  
  110.     CFRunLoopTimerRef qtimer = CFRunLoopTimerCreate( 
  111.                                   kCFAllocatorDefault,
  112.                                   CFAbsoluteTimeGetCurrent() + 1,
  113.                                   0,
  114.                                   0,
  115.                                   0,
  116.                                   delayedQuit,
  117.                                   NULL 
  118.                                   );
  119.     CFRunLoopAddTimer( CFRunLoopGetCurrent(), qtimer, kCFRunLoopCommonModes );
  120.  
  121.     return noErr;
  122. }
  123.  
  124. void setupSoundTimer(int period)
  125. {
  126.     if (soundTimer)
  127.     {
  128.         CFRunLoopRemoveTimer( CFRunLoopGetCurrent(), soundTimer, kCFRunLoopCommonModes );
  129.         CFRelease(soundTimer);
  130.         soundTimer = NULL;
  131.     }   
  132.     soundTimer = CFRunLoopTimerCreate( 
  133.                                   kCFAllocatorDefault,
  134.                                   CFAbsoluteTimeGetCurrent(),
  135.                                   period,
  136.                                   0,
  137.                                   0,
  138.                                   soundTimerCallback,
  139.                                   NULL 
  140.                                   );
  141.     CFRunLoopAddTimer( CFRunLoopGetCurrent(), soundTimer, kCFRunLoopCommonModes );
  142. }
  143.  
  144. void refreshPrefs()
  145. {
  146.     CFPreferencesAppSynchronize((CFStringRef)suiteID);
  147.  
  148.     NSString *s_period = 
  149.             (NSString *)CFPreferencesCopyAppValue((CFStringRef)MFKSASeconds, 
  150.                                                   (CFStringRef)suiteID);
  151.     int period = s_period ? [s_period intValue] : 20;
  152.     [s_period release];
  153.  
  154.     NSString *s_chargerAwareness = 
  155.             (NSString *)CFPreferencesCopyAppValue((CFStringRef)MFKSAWatchCharger, 
  156.                                                   (CFStringRef)suiteID);
  157.     shouldWatchCharger = s_chargerAwareness ? [s_chargerAwareness intValue] : YES;
  158.     [s_chargerAwareness release];
  159.  
  160.     setupSoundTimer(period);    
  161. }
  162.  
  163. // This routine is called when the preferences pane wants to tell
  164. // us that something has changed.  It simply calls refreshPrefs().
  165. void prefsChanged(CFNotificationCenterRef center, void *observer,
  166.             CFStringRef notificationName, const void *observedObject,
  167.             CFDictionaryRef userInfo)
  168. {
  169.     refreshPrefs(); 
  170. }
  171.  
  172. int main (int argc, const char * argv[])
  173. {
  174.     // Set up battery detection globals
  175.     IOMasterPort( MACH_PORT_NULL, &masterPort );
  176.     flagsString = [[NSString alloc] initWithCString:kIOBatteryFlagsKey];
  177.     
  178.     // Compile with NOISY defined to make KeepSoundAwake noisy instead of silent
  179.     // for debugging porpoises
  180. #ifdef NOISY
  181.     sound = 
  182.         [[NSSound alloc] initWithContentsOfFile: @"/System/Library/Sounds/Temple.aiff" 
  183.                          byReference: NO];
  184. #else
  185.     // Determined from a hex dump of a "blank" AIFF sound file. 
  186.     char AIFFbytes[54] = { 
  187.         'F', 'O', 'R', 'M', 0x00, 0x00, 0x00, 0x2E, 
  188.         'A', 'I', 'F', 'F', 'C', 'O', 'M', 'M',
  189.         0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x00, 
  190.         0x00, 0x00, 0x00, 0x10, 0x40, 0x0E, 0xAC, 0x44,
  191.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'S', 'S', 'N', 'D', 
  192.         0x00, 0x00, 0x00, 0x08, 
  193.         0x00, 0x00,    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  194.     };
  195.  
  196.     NSData *data = [[NSData alloc] initWithBytes:AIFFbytes length:54];
  197.     sound = [[NSSound alloc] initWithData:data];
  198.     [data release];
  199. #endif
  200.  
  201.     // Get our preferences and start the sound timer
  202.     refreshPrefs();
  203.  
  204.     // Listen for notification from the preference pane
  205.     void *observer;  // an arbitrary pointer, ignored
  206.     CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), 
  207.                                     observer, 
  208.                                     prefsChanged,
  209.                                     (CFStringRef)MFKSAPrefsChangedNotification, 
  210.                                     (CFStringRef)suiteID,
  211.                                     CFNotificationSuspensionBehaviorDeliverImmediately);
  212.  
  213.     // Intercept "quit" Apple Event
  214.     AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  215.             NewAEEventHandlerUPP(DoQuitApp), 0, FALSE);
  216.  
  217.     syslog(1, "starting up");
  218.     RunApplicationEventLoop();
  219.     
  220.     syslog(1, "cleaning up and exiting");
  221.     /* No fancy cleanup needed. */
  222.     
  223.     return 0;
  224. }
  225.